<?php
/**
* Classe étendue de PDO.
* 
* PHP version 5.1.6
*
* @package   Smartbok
* @author    Christophe Hebet <christophe.hebet@dgfip.finances.gouv.fr> 
* @copyright 2013 - DGFiP
*/

class SmartbokDB extends PDO
{
    const SBK_ERR_CONNEXIONDB = 'Echec de connexion à la base de donnée.';
    const SBK_ERREUR_EXEC_SQL = 'Une requête n\'a pas été exécutée';
    
    private $_tabReqSequence = array('oci' => 'SELECT #SEQUENCE#.CURRVAL as SEQUENCE FROM DUAL',
                                    'pgsql' => 'SELECT LAST_VALUE AS "SEQUENCE" FROM #SEQUENCE#',
                                    'mysql' => 'SELECT LAST_INSERT_ID() AS SEQUENCE'
    );
    
    protected $_sgbd;
    
    /**
     * précise si le charset de l'application doit être employé. Par défaut, les données en entrée et en sortie 
     * sont restituées telles quelles.
     * @var boolean
     */
    
    private $_forceCharset = false;
    
    /**
     * donne le nombre d'éléments traité par la requete
     * @var integer
     */
    private $_nbElements = 0;
    
    /**
     * Méthode d'exécution des requetes basiques. le traitement des données doit être fait au niveau des 
     * controllers ou à défaut des modèles.
     * 
     * Pour postgres, le nom de la séquence doit être précedé du nom de la base de données (BDD.NOM_SEQUENCE).
     * 
     * @param string $requete
     * @param array $tableauVars
     * @param string $nomSequence
     * @return multitype:
     */
    public function executerSql($requete, $tableauVars= null, $nomSequence= null) {
        $requete = ltrim($requete, "\x00..\x1F");
        $typeRequete = strtoupper(substr($requete, 0, strpos($requete, ' ')));
        if (isset($tableauVars) === true) {
            $tableauVars = $this->_cleanBind($tableauVars);
        }
        $resultat = $this->prepare($requete, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));    
        $execution = $resultat->execute($tableauVars);
        try {
        	
            if ($execution === true) {
                switch ($typeRequete) {
                    case 'SELECT':
                        $donnees = $resultat->fetchAll(PDO::FETCH_ASSOC);
                        $this->_nbElements = count($donnees);
                    break;
                    
                    case 'INSERT':
                        if (isset($nomSequence) === true) {
                            $reqSequence = $this->_tabReqSequence[$this->_sgbd];
                            if ($this->_sgbd !== 'mysql') {
                                $reqSequence = str_replace('#SEQUENCE#', $nomSequence, $reqSequence);
                            }
                            $donnees = $this->executerSql($reqSequence);
                            $donnees = $donnees[0]['SEQUENCE'];
                            $this->_nbElements = 1;
                            
                        }
                        else {
                        	$donnees = true;
                        }
                    break;
                    
                    case 'DELETE':
                    case 'UPDATE':
                        $donnees = $resultat->rowCount();
                        $this->_nbElements = $donnees;
                    break;
                    
                    default:
                        $donnees = array();
                        $this->_nbElements = 0;
                    break;
                }        
                return $donnees;
            }
            else {
                $this->_nbElements = 0;
                SmartbokMessage::addMessage(SmartbokMessage::ERR, self::SBK_ERREUR_EXEC_SQL.'('.$requete.')');
                print_r($tableauVars);
                return false;
            }
        } catch (Exception $e) {
            print_r($e);
        }
    }

    /**
     * function de récupération du nombre d'éléments traité par la requete
     */
    public function getNombreElements() {
        return $this->_nbElements;
    }
    
    /**
     * Méthode de nettoyage des variables bindées. Supprime les retours chariots inutiles.
     * @param array $vars
     * 
     * @return array
     */
    private function _cleanBind($vars) {
        foreach ($vars as $key => &$value) {
            if (is_string($value) === true)
            {
                $value = str_replace("\r\n", "\n", $value);
                $value = str_replace("\r", "\n", $value);
                $value = trim($value);
            }
            $vars[$key] = $value;
        }
        return $vars;
    }
    
    /**
     * Fonction de controle de l'encodage d'une chaine en utf8.
     *
     * la fonction test les caractères d'une chaine 1 à 1 jusqu'à ce qu'elle puisse déterminer si la chaine 
     * est en utf8 ou non.
     *
     * @param string  $str
     */
    public static function isUtf8($str) {
        $c = 0;
        $b = 0;
        $bits = 0;
        $len = strlen($str);
        for($i = 0; $i < $len; $i++){
            $c = ord($str[$i]);
            if($c > 128){
                if(($c >= 254)) return false;
                elseif($c >= 252) $bits = 6;
                elseif($c >= 248) $bits = 5;
                elseif($c >= 240) $bits = 4;
                elseif($c >= 224) $bits = 3;
                elseif($c >= 192) $bits = 2;
                else return false;
                if(($i + $bits) > $len) return false;
                while($bits > 1){
                    $i++;
                    $b = ord($str[$i]);
                    if($b < 128 || $b > 191) return false;
                    $bits--;
                }
            }
        }
        return true;
    }
    
    /**
     * Cette méthode permet de trouve le bon encodage a appliquer sur une chaine de charactères lrosque l'un des 
     * éléments de l'architecture n'est pas en utf8. 
     *  
     * @param array $donnees
     */
    public static function decodageUtf8($donnees) {
        foreach ($donnees as $cle => &$data)
        {
            foreach ($data as $key => &$value)
            {
                if (self::isUtf8($value) === false) {
                    $value = htmlspecialchars(utf8_encode($value), ENT_NOQUOTES);
                }
                elseif(self::is_utf8(utf8_decode($value)) === false) {
                    $value = htmlspecialchars($value, ENT_NOQUOTES);
                }
                else {
                    $value = htmlspecialchars(utf8_decode($value), ENT_NOQUOTES);
                }
            }
        }
        return $donnees;
    }
}